{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## original algorithm: [day 28 - convex hull](https://github.com/coells/100days/blob/master/day 28 - convex hull.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## refactored algorithm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def extend(points, u, v, hull):\n",
    "    if not len(points):\n",
    "        return\n",
    "\n",
    "    # find W as the furthest point from U-V\n",
    "    w = points[np.argmin(np.cross(points - u, v - u))]\n",
    "    p = points - w\n",
    "\n",
    "    # extend hull for U-W and V-W\n",
    "    extend(points[np.cross(p, v - w) < 0], w, v, hull)\n",
    "    hull.append(w)\n",
    "    extend(points[np.cross(p, u - w) > 0], u, w, hull)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def convex_hull(points):\n",
    "    # U is left-most hull point, V is right-most hull point\n",
    "    u = points[np.argmin(points[:, 0])]\n",
    "    v = points[np.argmax(points[:, 0])]\n",
    "    w = np.cross(points - u, v - u)\n",
    "\n",
    "    # recurse on hull construction\n",
    "    hull = [v]\n",
    "    extend(points[w < 0], u, v, hull)\n",
    "    hull.append(u)\n",
    "    extend(points[w > 0], v, u, hull)\n",
    "    hull.append(v)\n",
    "\n",
    "    return hull"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## benchmark"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1000 loops, best of 3: 987 µs per loop\n"
     ]
    }
   ],
   "source": [
    "%timeit convex_hull(np.random.rand(10**3, 2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100 loops, best of 3: 2.35 ms per loop\n"
     ]
    }
   ],
   "source": [
    "%timeit convex_hull(np.random.rand(10**4, 2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100 loops, best of 3: 13.8 ms per loop\n"
     ]
    }
   ],
   "source": [
    "%timeit convex_hull(np.random.rand(10**5, 2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10 loops, best of 3: 167 ms per loop\n"
     ]
    }
   ],
   "source": [
    "%timeit convex_hull(np.random.rand(10**6, 2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 loop, best of 3: 1.98 s per loop\n"
     ]
    }
   ],
   "source": [
    "%timeit convex_hull(np.random.rand(10**7, 2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
